/**
 * \brief  GRUB multi-boot information handling
 * \author Christian Helmuth
 * \date   2006-05-10
 */

/*
 * Copyright (C) 2006-2009 Christian Helmuth
 * Genode Labs, Feske & Helmuth Systementwicklung GbR
 *
 * This file is part of the Genode OS framework, which is distributed
 * under the terms of the GNU General Public License version 2.
 */

#include <base/printf.h>
#include <multiboot.h>
#include <util/misc_math.h>

using namespace Genode;

namespace Mb_info {
#include "mb_info.h"
}

/* DEBUGGING */
static bool verbose = false;


void Multiboot_info::print_debug()
{
	Mb_info::mb_info_t *mbi = (Mb_info::mb_info_t *)_mb_info;

	printf("  flags = %b %s\n", mbi->flags,
	       mbi->flags & MB_CMDLINE ? "CMDLINE" : "");
	printf("  mem_lower = %08p\n", mbi->mem_lower);
	printf("  mem_upper = %08p\n", mbi->mem_upper);
	printf("  boot_device = %x\n", mbi->boot_device);
//	printf("  cmdline = %08p \"%s\"\n", mbi->cmdline, (char *)mbi->cmdline);
	printf("  mods_count = %d\n", mbi->mods_count);
	printf("  mods_addr = %08p\n", mbi->mods_addr);

	unsigned i = 0;
	Mb_info::mb_mod_t *mods = (Mb_info::mb_mod_t *)mbi->mods_addr;
	for (i = 0; i < mbi->mods_count; i++)
		printf("    mod[%02d]  [%08p,%08p) %s\n", i,
		       (void *)mods[i].mod_start,
		       (void *)(mods[i].mod_end),
		       (char *)mods[i].cmdline);

	printf("  mmap_length = %x\n", mbi->mmap_length);
	printf("  mmap_addr = %x\n", mbi->mmap_addr);
	printf("  drives_length = %x\n", mbi->drives_length);
	printf("  drives_addr = %x\n", mbi->drives_addr);
	printf("  config_table = %x\n", mbi->config_table);
	printf("  boot_loader_name = %x\n", mbi->boot_loader_name);
	printf("  apm_table = %x\n", mbi->apm_table);
	printf("  vbe_ctrl_info = %08p\n", mbi->vbe_ctrl_info);
	printf("  vbe_mode_info = %08p\n", mbi->vbe_mode_info);
	printf("  vbe_mode = %x\n", mbi->vbe_mode);
	printf("  vbe_interface_seg = %x\n", mbi->vbe_interface_seg);
	printf("  vbe_interface_off = %x\n", mbi->vbe_interface_off);
	printf("  vbe_interface_len = %x\n", mbi->vbe_interface_len);
}


unsigned Multiboot_info::num_modules()
{
	Mb_info::mb_info_t *mbi = (Mb_info::mb_info_t *)_mb_info;

	return mbi->mods_count;
}


Rom_module Multiboot_info::get_module(unsigned num)
{
	Mb_info::mb_info_t *mbi = (Mb_info::mb_info_t *)_mb_info;
	Mb_info::mb_mod_t *mods = (Mb_info::mb_mod_t *)mbi->mods_addr;

	/* num exceeds number of modules */
	if (!(num < mbi->mods_count)) return Rom_module();

	/* invalid module -- maybe returned earlier */
	if (!mods[num].cmdline) return Rom_module();

	Rom_module ret = Rom_module(mods[num].mod_start,
	                            mods[num].mod_end - mods[num].mod_start,
	                            (char *)mods[num].cmdline);

	/* mark module as invalid */
	mods[num].cmdline = 0;

	return ret;
}


bool Multiboot_info::check_module(unsigned num, addr_t *start, addr_t *end)
{
	Mb_info::mb_info_t *mbi = (Mb_info::mb_info_t *)_mb_info;
	Mb_info::mb_mod_t *mods = (Mb_info::mb_mod_t *)mbi->mods_addr;

	/* num exceeds number of modules */
	if (!(num < mbi->mods_count)) return false;

	*start = mods[num].mod_start;
	*end = mods[num].mod_end;

	return true;
}

/**
 * Constructor
 */
Multiboot_info::Multiboot_info(void *mb_info)
: _mb_info(mb_info)
{
	Mb_info::mb_info_t *mbi = (Mb_info::mb_info_t *)_mb_info;
	Mb_info::mb_mod_t *mods = (Mb_info::mb_mod_t *)mbi->mods_addr;

	/* strip path and arguments from module name */
	for (unsigned i = 0; i < mbi->mods_count; i++) {
		char *name = (char *)mods[i].cmdline;
		for (char *c = name; *c != 0; c++) {
			if (*c == '/') name = c + 1;
			if (*c == ' ') {
				*c = 0;
				break;
			}
		}
		mods[i].cmdline = (addr_t)name;
	}

	if (verbose) {
		printf("Multi-boot info with %d modules @ %p.\n",
		       mbi->mods_count, _mb_info);
		print_debug();
	}
}
